import { ts,  NodePredicate } from 'qvog-dsl';
import { native, HomeCheckHint } from 'qvog-dsl';

const SEVERITY = 2;
const DOC_PATH = 'docs/eqeqeq.md';
const DESCRIPTION = 'Require the use of === and !==';

export const Statement: NodePredicate<ts.BinaryExpression> = {
  is(node: ts.Node): node is ts.BinaryExpression {
    return ts.isBinaryExpression(node) && [
      ts.SyntaxKind.EqualsEqualsToken,
      ts.SyntaxKind.ExclamationEqualsToken
    ].includes(node.operatorToken.kind);
  }
};

export default native<HomeCheckHint>()()
  .match(Statement)
  .when((node: ts.BinaryExpression): boolean => {
    if (areLiteralsAndSameType(node)) {
      return false;
    }

    if (isTypeOfBinary(node)) {
      return false;
    }

    return true;
  })
  .report({
    severity: SEVERITY,
    description: DESCRIPTION,
    docPath: DOC_PATH,
  });

function areLiteralsAndSameType(node: ts.BinaryExpression): boolean {
  const isLiteral = (n: ts.Node): boolean =>
    ts.isLiteralExpression(n) ||
    n.kind === ts.SyntaxKind.TrueKeyword ||
    n.kind === ts.SyntaxKind.FalseKeyword;

  return isLiteral(node.left) &&
    isLiteral(node.right) &&
    typeof getLiteralValue(node.left) === typeof getLiteralValue(node.right);
}

function getLiteralValue(node: ts.Node): string | number | boolean | null {
  if (ts.isStringLiteral(node)) { return node.text };
  if (ts.isNumericLiteral(node)) { return parseFloat(node.text) };
  if (node.kind === ts.SyntaxKind.TrueKeyword) { return true };
  if (node.kind === ts.SyntaxKind.FalseKeyword) { return false };
  return null;
}

function isTypeOfBinary(node: ts.BinaryExpression): boolean {
  return ts.isTypeOfExpression(node.left) ||
    ts.isTypeOfExpression(node.right);
}